﻿using Bikiball.Game;
using Bikiball.Game;
using Bikiball.Graphic.Vextex;
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.Toolkit.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Texture3D = SharpDX.Toolkit.Graphics.Texture3D;
using SamplerState = SharpDX.Toolkit.Graphics.SamplerState;

namespace Bikiball.Graphic.Constructor
{
    public  static class Map3DConstructor
    {
        internal struct Facto
        {
            internal int X;
            internal int Z;
        }

        public static Texture3D MapTexture = null;
        public static Effect MapEffect = null;

        public static void LoadContent()
        {
            MapTexture = GraphicEngine.Content.Load<Texture3D>("map.dds");
            MapEffect = GraphicEngine.Content.Load<Effect>("Map.fxo");
        }
        public static MapGraphicProperty Construct(Map map)
        {
            MapGraphicProperty gp = new MapGraphicProperty();
            // Creates a basic effect
           /* gp.Effect = new BasicEffect(GraphicEngine.GraphicsDevice)
            {
                VertexColorEnabled = true,
                View = Matrix.LookAtLH(new Vector3(0.0001f, -5, -5), new Vector3(0, 0, 0), Vector3.UnitY),
                Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4.0f, (float)(GraphicEngine.GraphicsDevice.BackBuffer).Width / (GraphicEngine.GraphicsDevice.BackBuffer).Height, 0.1f, 100.0f),
                World = Matrix.Zero
            };*/
            SetNbVertices(gp, map);
            int[] indices = new int[gp.NumIndices];
            VertexPositionTexture3D[] vertices = new VertexPositionTexture3D[gp.NumVertices];
            int i = 0;
            int v = 0;
            for (int x = 0; x < map.SizeX; x++)
            {
                for (int y = 0; y < map.SizeY; y++)
                {
                    for (int z = 0; z < map.SizeZ; z++)
                    {
                        float imageLayer = ((int)map.MapCases[x, y, z] * 8.0f - 4.0f) / 256.0f;
                        switch (map.MapCases[x, y, z])
                        {
                            case MapCase.Empty:
                                break;
                            /*case MapCase.Up:
                                vertices[v] =  new VertexPositionTexture3D(new Vector3(x + 1, y + (v / 10), z + 2), new Vector3(1.0f, 2.0f, imageLayer)); // Bottom
                                vertices[v + 1] = new VertexPositionTexture3D(new Vector3(x, y + (v / 10), z + 2), new Vector3(0.0f, 2.0f, imageLayer));
                                vertices[v + 2] = new VertexPositionTexture3D(new Vector3(x, y + (v / 10), z), new Vector3(0.0f, 0.0f, imageLayer));
                                vertices[v + 3] = new VertexPositionTexture3D(new Vector3(x + 1, y + (v / 10), z), new Vector3(1.0f, 0.0f, imageLayer));
                                indices[i] = v; indices[i + 1] = (v + 1); indices[i + 2] = (v + 2);
                                indices[i + 3] = (v + 3); indices[i + 4] = v; indices[i + 5] = (v + 2);
                                v += 4;
                                i += 6;
                                break;*/
                            case MapCase.Wall:
                                // Bottom Vertexes
                                vertices[v] =  new VertexPositionTexture3D(new Vector3(x + 1, y, z), new Vector3(0.0f, 1.0f, imageLayer)); 
                                vertices[v + 1] = new VertexPositionTexture3D(new Vector3(x + 1, y + 1, z), new Vector3(0.0f, 0.0f, imageLayer));
                                vertices[v + 2] = new VertexPositionTexture3D(new Vector3(x + 1, y, z + 1), new Vector3(1.0f, 1.0f, imageLayer));
                                vertices[v + 3] = new VertexPositionTexture3D(new Vector3(x + 1, y + 1, z + 1), new Vector3(1.0f, 0.0f, imageLayer));
                                // Top Vertexes
                                vertices[v + 4] = new VertexPositionTexture3D(new Vector3(x, y, z + 1), new Vector3(0.0f, 1.0f, imageLayer));
                                vertices[v + 5] = new VertexPositionTexture3D(new Vector3(x, y, z), new Vector3(1.0f, 1.0f, imageLayer));
                                vertices[v + 6] = new VertexPositionTexture3D(new Vector3(x, y + 1, z + 1), new Vector3(0.0f, 0.0f, imageLayer));
                                vertices[v + 7] = new VertexPositionTexture3D(new Vector3(x, y + 1, z), new Vector3(1.0f, 0.0f, imageLayer));
                                //Face Devant
                                indices[i] = v + 4; indices[i + 1] = (v + 6); indices[i + 2] = (v + 2);
                                indices[i + 3] = (v + 3); indices[i + 4] = v + 2; indices[i + 5] = (v + 6);
                                //Face Derrière
                                
                                indices[i + 6] = v + 7; indices[i + 7] = v + 5; indices[i + 8] = v;
                                indices[i + 9] = v + 7; indices[i + 10] = v; indices[i + 11] = v + 1;
                                //Face Gauche
                                indices[i + 12] = v + 5; indices[i + 13] = (v + 6); indices[i + 14] = (v + 4);
                                indices[i + 15] = (v + 6); indices[i + 16] = v + 5; indices[i + 17] = (v + 7);
                                //Face Droite
                                indices[i + 18] = v; indices[i + 19] = (v + 2); indices[i + 20] = (v + 3);
                                indices[i + 21] = v + 1; indices[i + 22] = v; indices[i + 23] = (v + 3);
                                v += 8;
                                i += 24;
                                break;
                            default:
                                vertices[v] =  new VertexPositionTexture3D(new Vector3(x + 1, y, z + 1), new Vector3(1.0f, 1.0f, imageLayer)); // Bottom
                                vertices[v + 1] = new VertexPositionTexture3D(new Vector3(x, y, z + 1), new Vector3(0.0f, 1.0f, imageLayer));
                                vertices[v + 2] = new VertexPositionTexture3D(new Vector3(x, y, z), new Vector3(0.0f, 0.0f, imageLayer));
                                vertices[v + 3] = new VertexPositionTexture3D(new Vector3(x + 1, y, z), new Vector3(1.0f, 0.0f, imageLayer));
                                indices[i] = v; indices[i + 1] = (v + 1); indices[i + 2] = (v + 2);
                                indices[i + 3] = (v + 3); indices[i + 4] = v; indices[i + 5] = (v + 2);
                                v += 4;
                                i += 6;
                                break;
                        }
                    }
                }
            }
            gp.Effect = MapEffect;
            gp.VertexBuffer = SharpDX.Toolkit.Graphics.Buffer.Vertex.New<VertexPositionTexture3D>(GraphicEngine.GraphicsDevice, vertices);
            gp.IndexBuffer = SharpDX.Toolkit.Graphics.Buffer.Index.New(GraphicEngine.GraphicsDevice, indices);
            gp.NumVertices = vertices.Length;
            gp.Texture = MapTexture;
            gp.VertexStride = VertexPositionTexture3D.Size;
            // Create an input layout from the vertices
            gp.InputLayout = VertexInputLayout.FromBuffer(0, (Buffer<VertexPositionTexture3D>) gp.VertexBuffer);
            // Create SamplerState to Wrap U/V texture Coordinates
            SamplerStateDescription spdesc = SamplerStateDescription.Default();
            spdesc.AddressU = TextureAddressMode.Wrap;
            spdesc.AddressV = TextureAddressMode.Wrap;
            gp.SamplerState = SamplerState.New(GraphicEngine.GraphicsDevice, spdesc);
            return gp;
        }

        public static MapGraphicProperty ConstructOptimized(Map map)
        {
            MapGraphicProperty gp = new MapGraphicProperty();
            bool[, ,] mapDone = new bool[map.SizeX,map.SizeY,map.SizeZ];

            for (int x = 0; x < map.SizeX; x++)
            {
                for (int y = 0; y < map.SizeY; y++)
                {
                    for (int z = 0; z < map.SizeZ; z++)
                    {
                        mapDone[x, y, z] = false;
                    }
                }
            }
            SetNbServicesOptimized(gp, map);
            int[] indices = new int[gp.NumIndices];
            VertexPositionTexture3D[] vertices = new VertexPositionTexture3D[gp.NumVertices];
            int ind = 0;
            int v = 0;
            for (int x = 0; x < map.SizeX; x++)
            {
                for (int y = 0; y < map.SizeY; y++)
                {
                    for (int z = 0; z < map.SizeZ; z++)
                    {
                        #region Facto
                        if (mapDone[x, y, z])
                            continue;
                        Facto facto = TryFact(map, mapDone, x, y, z);
                        for (int i = 0; i < facto.X; i++)
                            for (int j = 0; j < facto.Z; j++)
                                mapDone[x + i, y, z + j] = true;
                        #endregion
                        #region Vertices & Indices
                        float imageLayer = ((int)map.MapCases[x, y, z] * 8.0f - 4.0f) / 256.0f;
                        switch (map.MapCases[x, y, z])
                        {
                            case MapCase.Empty:
                                break;
                            case MapCase.Wall:
                                //Front
                                vertices[v] = new VertexPositionTexture3D(new Vector3(x, y, z + facto.Z), new Vector3(0.0f, 0.0f, imageLayer));
                                vertices[v + 1] = new VertexPositionTexture3D(new Vector3(x, y + 1, z + facto.Z), new Vector3(0.0f, 1.0f, imageLayer));
                                vertices[v + 2] = new VertexPositionTexture3D(new Vector3(x + facto.X, y, z + facto.Z), new Vector3(facto.X, 0.0f, imageLayer));
                                vertices[v + 3] = new VertexPositionTexture3D(new Vector3(x + facto.X, y + 1, z + facto.Z), new Vector3(facto.X, 1.0f, imageLayer));
                                indices[ind] = v; indices[ind + 1] = (v + 1); indices[ind + 2] = (v + 2);
                                indices[ind + 3] = (v + 3); indices[ind + 4] = v + 2; indices[ind + 5] = (v + 1);
                                v += 4;
                                ind += 6;
                                // Back
                                vertices[v] = new VertexPositionTexture3D(new Vector3(x, y, z), new Vector3(0, 1.0f, imageLayer));
                                vertices[v + 1] = new VertexPositionTexture3D(new Vector3(x, y + 1, z), new Vector3(0, 0.0f, imageLayer));
                                vertices[v + 2] = new VertexPositionTexture3D(new Vector3(x + facto.X, y, z), new Vector3(facto.X, 1.0f, imageLayer));
                                vertices[v + 3] = new VertexPositionTexture3D(new Vector3(x + facto.X, y + 1, z), new Vector3(facto.X, 0.0f, imageLayer));
                                indices[ind] = v + 2; indices[ind + 1] = (v + 1); indices[ind + 2] = (v);
                                indices[ind + 3] = (v + 1); indices[ind + 4] = v + 2; indices[ind + 5] = (v + 3);
                                v += 4;
                                ind += 6;
                                // Left
                                vertices[v] = new VertexPositionTexture3D(new Vector3(x, y, z), new Vector3(0.0f, 0.0f, imageLayer));
                                vertices[v + 1] = new VertexPositionTexture3D(new Vector3(x, y + 1, z), new Vector3(1.0f, 0.0f, imageLayer));
                                vertices[v + 2] = new VertexPositionTexture3D(new Vector3(x, y, z + facto.Z), new Vector3(0.0f, facto.Z, imageLayer));
                                vertices[v + 3] = new VertexPositionTexture3D(new Vector3(x, y + 1, z + facto.Z), new Vector3(1.0f, facto.Z, imageLayer));
                                indices[ind] = v; indices[ind + 1] = (v + 1); indices[ind + 2] = (v + 2);
                                indices[ind + 3] = (v + 3); indices[ind + 4] = v + 2; indices[ind + 5] = (v + 1);
                                v += 4;
                                ind += 6;
                                // Right
                                vertices[v] = new VertexPositionTexture3D(new Vector3(x + facto.X, y, z), new Vector3(0.0f, 0.0f, imageLayer));
                                vertices[v + 1] = new VertexPositionTexture3D(new Vector3(x + facto.X, y + 1, z), new Vector3(1.0f, 0.0f, imageLayer));
                                vertices[v + 2] = new VertexPositionTexture3D(new Vector3(x + facto.X, y, z + facto.Z), new Vector3(0.0f, facto.Z, imageLayer));
                                vertices[v + 3] = new VertexPositionTexture3D(new Vector3(x + facto.X, y + 1, z + facto.Z), new Vector3(1.0f, facto.Z, imageLayer));
                                //Face Droite
                                indices[ind] = v + 2; indices[ind + 1] = (v + 1); indices[ind + 2] = (v);
                                indices[ind + 3] = (v + 1); indices[ind + 4] = v + 2; indices[ind + 5] = (v + 3);
                                v += 4;
                                ind += 6;
                                break;
                            default:
                                vertices[v] = new VertexPositionTexture3D(new Vector3(x + facto.X, y, z + facto.Z), new Vector3(facto.X, facto.Z, imageLayer)); // Bottom
                                vertices[v + 1] = new VertexPositionTexture3D(new Vector3(x, y, z + facto.Z), new Vector3(0.0f, facto.Z, imageLayer));
                                vertices[v + 2] = new VertexPositionTexture3D(new Vector3(x, y, z), new Vector3(0.0f, 0.0f, imageLayer));
                                vertices[v + 3] = new VertexPositionTexture3D(new Vector3(x + facto.X, y, z), new Vector3(facto.X, 0.0f, imageLayer));
                                indices[ind] = v; indices[ind + 1] = (v + 1); indices[ind + 2] = (v + 2);
                                indices[ind + 3] = (v + 3); indices[ind + 4] = v; indices[ind + 5] = (v + 2);
                                v += 4;
                                ind += 6;
                                break;
                        }
                        #endregion
                    }
                }
            }
            gp.Effect = MapEffect;
            gp.VertexBuffer = SharpDX.Toolkit.Graphics.Buffer.Vertex.New<VertexPositionTexture3D>(GraphicEngine.GraphicsDevice, vertices);
            gp.IndexBuffer = SharpDX.Toolkit.Graphics.Buffer.Index.New(GraphicEngine.GraphicsDevice, indices);
            gp.NumVertices = vertices.Length;
            gp.Texture = MapTexture;
            gp.VertexStride = VertexPositionTexture3D.Size;
            // Create an input layout from the vertices
            gp.InputLayout = VertexInputLayout.FromBuffer(0, (Buffer<VertexPositionTexture3D>)gp.VertexBuffer);
            // Create SamplerState to Wrap U/V texture Coordinates
            SamplerStateDescription spdesc = SamplerStateDescription.Default();
            spdesc.AddressU = TextureAddressMode.Wrap;
            spdesc.AddressV = TextureAddressMode.Wrap;
            spdesc.Filter = Filter.Anisotropic;
            gp.SamplerState = SamplerState.New(GraphicEngine.GraphicsDevice, spdesc);
            return gp;
        }

        private static void SetNbServicesOptimized(GraphicProperty gp, Map map)
        {
            gp.NumVertices = 0;
            gp.NumIndices = 0;
            gp.NumTriangles = 0;
            bool[, ,] mapDone = new bool[map.SizeX, map.SizeY, map.SizeZ];

            int x = 0;
            int y = 0;
            int z = 0;
            for (x = 0; x < map.SizeX; x++)
            {
                for (y = 0; y < map.SizeY; y++)
                {
                    for (z = 0; z < map.SizeZ; z++)
                    {
                        mapDone[x, y, z] = false;
                    }
                }
            }

            x = 0;
            y = 0;
            z = 0;
            for (y = 0; y < map.SizeY; y++)
            {
                for (x = 0; x < map.SizeX; x++)
                {
                    for (z = 0; z < map.SizeZ; z++)
                    {
                        if (mapDone[x, y, z])
                            continue;
                        Facto facto = TryFact(map, mapDone, x, y, z);
                        for (int i = 0; i < facto.X; i++)
                            for (int j = 0; j < facto.Z; j++)
                                mapDone[x + i, y, z + j] = true;
                        switch (map.MapCases[x, y, z])
                        {
                            case MapCase.Empty:
                                break;
                            case MapCase.Wall:
                                gp.NumVertices += 16;
                                gp.NumIndices += 24;
                                gp.NumTriangles += 8;
                                break;
                            default:
                                gp.NumVertices += 4;
                                gp.NumIndices += 6;
                                gp.NumTriangles += 2;
                                break;
                        }
                    }
                }
            }
        }

        private static Facto TryFact(Game.Map map, bool[, ,] mapDone, int x, int y, int z)
        {
            Facto facto = new Facto();
            MapCase actualCase = map.MapCases[x, y, z];
            Bool tryAgain = true;

            int i = 1;
            int j = 1;
            while (tryAgain)
            {
                try
                {
                    if (!mapDone[x + i, y, z] && map.MapCases[x + i, y, z] == actualCase)
                    {
                        i++;
                    }
                    else
                    {
                        tryAgain = false;
                    }
                }
                catch (Exception e)
                {
                    tryAgain = false;
                }
            }
            tryAgain = true;
            bool lineOk = true;
            facto.X = i;
            while (tryAgain)
            {
                lineOk = true;
                try
                {
                    for (int it = 0; it < facto.X; it++)
                    {
                        if (mapDone[x + it, y, z + j] || map.MapCases[x + it, y, z + j] != actualCase)
                            lineOk = false;
                    }
                    if (lineOk)
                        j++;
                    else
                        tryAgain = false;
                }
                catch (Exception e)
                {
                    tryAgain = false;
                }
            }
            facto.Z = j;
            return facto;
        }

        private static void SetNbVertices(GraphicProperty gp, Map map)
        {
            gp.NumVertices = 0;
            gp.NumIndices = 0;
            gp.NumTriangles = 0;
            for (int x = 0; x < map.SizeX; x++)
            {
                for (int y = 0; y < map.SizeY; y++)
                {
                    for (int z = 0; z < map.SizeZ; z++)
                    {
                        switch (map.MapCases[x, y, z])
                        {
                            case MapCase.Empty:
                                break;
                            case MapCase.Wall:
                                gp.NumVertices += 8;
                                gp.NumIndices += 24;
                                gp.NumTriangles += 8;
                                /*
                                gp.NumVertices += 16;
                                gp.NumIndices += 24;
                                gp.NumTriangles += 8;*/
                                break;
                            default:
                                gp.NumVertices += 4;
                                gp.NumIndices += 6;
                                gp.NumTriangles += 2;
                                break;
                               
                        }
                    }
                }
            }
        }
    }
}
